/**
 * Wihidum - Efficient Broker Hierarchy for Publish Subscribe Middleware
 *
 * License      :   Apache License 2.0
 * Home Page    :   http://code.google.com/p/wihidum/
 * Mailing List :   wihidum@googlegroups.com
 */

package lk.mrt.cse.wihidum.client;

import lk.mrt.cse.wihidum.util.*;
import org.apache.axiom.om.*;
import org.apache.axis2.*;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.wsdl.WSDLConstants;
import org.apache.axis2.client.*;
import org.apache.log4j.*;

import org.apache.axis2.client.ServiceClient;
import org.apache .axiom.soap.*;
import org.apache.axis2.context.*;

import javax.xml.namespace.QName;
import java.util.UUID;

/**
 * This exposes methods for subscribers to send subscription related messages to the broker.
 * Subscribing web services or client applications can use this class for sending subscriptions.
 * Hence, this acts as an interface between the subscribing applications and the broker service.
 */
public class SubscriptionClient
{
    private static final Logger logger = Logger.getLogger (SubscriptionClient.class);

    private SubscriptionClientBean clientbean;

    public SubscriptionClient (SubscriptionClientBean clientBean)
    {
        this.clientbean = clientBean;
    }

    public String subscribe ()
    {
        String brokerAddress=clientbean.getBrokerAddress ().getAddress();
        if(BrokerList.isEmpty())
        {
            String adminAddress=clientbean.getAdminAddress().getAddress();
            String subscriberAddress=clientbean.getSubscriberAddress().getAddress();
            BrokerRetrievalClient brokerRetrievalClient=new BrokerRetrievalClient();
            brokerRetrievalClient.retrieveBrokers(adminAddress,subscriberAddress);
            
        }
        if(BrokerList.isEmpty())
        {
            logger.info("Broker List is still empty. Therefore the broker address provide from the UI is taken.");
        }
        else
        {
            BrokerList.shuffleBrokerList();
            brokerAddress=BrokerList.getTopBroker();
            logger.info("Broker Address is taken from client's broker list. It is: "+brokerAddress);
        }

        try
        {
            //logger.info ("Sending subscription message");

            ServiceClient serviceClient = new ServiceClient ();
            
            //creating in out operating client
            OperationClient opClient = serviceClient.createClient(ServiceClient.ANON_OUT_ONLY_OP);//ANON_OUT_IN_OP);
            
            //creating message context
            MessageContext outMsgCtx = new MessageContext();
            Options opts = outMsgCtx.getOptions();
            //setting properties into option
            //opts.setTo(clientbean.getBrokerAddress ());
            opts.setTo(new EndpointReference(brokerAddress));
            logger.info("Subscription request set to Subscrption Manager: "+brokerAddress);
            
            opts.setAction(WihidumConstants.Operations.SUBSCRIBE);
            opts.setFrom(clientbean.getSubscriberAddress());
            opts.setReplyTo(clientbean.getListenerAddress());

            //serviceClient.engageModule("addressing");
            String SOAPVersion = opts.getSoapVersionURI();
            if (SOAPVersion==null)
                SOAPVersion=SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI;
            SOAPEnvelope subscriptionEnvelope=createSubscriptionEnvelope(SOAPVersion);

            outMsgCtx.setEnvelope(subscriptionEnvelope);
            opClient.addMessageContext(outMsgCtx);
            opClient.execute(true);
            
            return "Subscription request sent to Subscrption Manager: "+brokerAddress;
        }
        catch(Exception e)
        {        e.printStackTrace();
                 return "Error in sending subscription";
        }

    }

     /**
     * Following method creates and returns a subscription request message according to the WS Eventing specification
     */
    private SOAPEnvelope createSubscriptionEnvelope(String SOAPURI) throws Exception
    {
        //generate the uuid of the subscription message using the java.util.UUID
        String uuid= UUID.randomUUID().toString();

        SOAPFactory factory=null;
        if (SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(SOAPURI))
              factory = OMAbstractFactory.getSOAP11Factory();
        else if (SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(SOAPURI))
              factory = OMAbstractFactory.getSOAP12Factory();
        else throw new Exception("Unknown SOAP version");
        SOAPEnvelope soapEnvelope=factory.getDefaultEnvelope();
        //declare the used namespaces in the SOAP Envelope
        OMNamespace wseventingNS=factory.createOMNamespace(WSEventingConstants.EVENTING_NAMESPACE,WSEventingConstants.EVENTING_PREFIX);
        soapEnvelope.declareNamespace(wseventingNS);
        OMNamespace wsaNS=factory.createOMNamespace(WSEventingConstants.ADDRESSING_NAMESPACE,WSEventingConstants.ADDRESSING_PREFIX);
        soapEnvelope.declareNamespace(wsaNS);
        OMNamespace wihidumNS=factory.createOMNamespace(WSEventingConstants.WIHIDUM_NAMESPACE,WSEventingConstants.WHIDUM_NAMESPACE_PREFIX);
        soapEnvelope.declareNamespace(wihidumNS);
        SOAPHeader soapHeader=soapEnvelope.getHeader();


        SOAPBody soapBody=soapEnvelope.getBody();

        //create the subscription element for the body
        OMElement subscriptionElement =factory.createOMElement(WSEventingConstants.ElementNames.Subscribe, wseventingNS);
            
            //Delivery Element
            OMElement deliveryElement =factory.createOMElement(WSEventingConstants.ElementNames.Delivery, wseventingNS);

            OMElement notifyToElement=factory.createOMElement(WSEventingConstants.ElementNames.NotifyTo,wseventingNS);

            //add delivery EPR as to where the notifications should be sent
            OMElement notifyToAddressElement=factory.createOMElement(WSEventingConstants.ElementNames.Address,wsaNS);
            notifyToAddressElement.setText(clientbean.getListenerAddress().getAddress());

            notifyToElement.addChild(notifyToAddressElement);

            //add clientID as the reference property
            OMElement referencePropertieElement=factory.createOMElement(WSEventingConstants.ElementNames.ReferenceProperties,wsaNS);

            OMElement subscriptionIDElement=factory.createOMElement(WSEventingConstants.ElementNames.SubscriptionID,wihidumNS);
            subscriptionIDElement.setText(clientbean.getSubscriptionID());

            referencePropertieElement.addChild(subscriptionIDElement);

            notifyToElement.addChild(referencePropertieElement);

            deliveryElement.addChild(notifyToElement);

            subscriptionElement.addChild(deliveryElement);

            //todo: define a format for filter dialact

            if (clientbean.getTopic() != null)
            {
                String filter = clientbean.getTopic();
                OMElement filterElement =factory.createOMElement(WSEventingConstants.ElementNames.Filter, wseventingNS);
                filterElement.setText(filter);

                subscriptionElement.addChild(filterElement);
            }

        soapBody.addChild(subscriptionElement); 
        return soapEnvelope;
    }

}